using System.Collections.Specialized;
using System.Web.Security;
using Be.Timvw.Framework.Security;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Rhino.Mocks;

namespace Be.Timvw.Framework.Web.Security
{
    [TestClass]
    public class WhenValidatingOneTimePasswordMembershipProvider
    {
        #region Infrastructure for all tests in this class

        private IMembershipProvider membershipProvider;
        private MockRepository repository;

        [TestInitialize]
        public void BeforeEachMethod()
        {
            this.repository = new MockRepository();
            this.membershipProvider = this.repository.StrictMock<IMembershipProvider>();
            MockMembershipProviderWrapper.MembershipProvider = this.membershipProvider;
        }

        [TestCleanup]
        public void AfterEachMethod()
        {
            MockMembershipProviderWrapper.MembershipProvider = null;

            this.repository.Playback().Dispose();
            this.repository.VerifyAll();
            this.repository = null;
        }

        public NameValueCollection GetConfig()
        {
            NameValueCollection config = new NameValueCollection();
            config.Add(OneTimePasswordMembershipProvider.ProviderTypeKey, typeof(MockMembershipProviderWrapper).AssemblyQualifiedName);
            return config;
        }

        public OneTimePasswordMembershipProvider CreateAndInitializeOneTimePasswordMembershipProvider()
        {
            return this.CreateAndInitializeOneTimePasswordMembershipProvider("/", this.GetConfig());
        }

        public OneTimePasswordMembershipProvider CreateAndInitializeOneTimePasswordMembershipProvider(string name, NameValueCollection config)
        {
            string expectedName = name;
            NameValueCollection expectedConfig = config;

            using(this.repository.Record())
            {
                Expect.Call(delegate { this.membershipProvider.Initialize(expectedName, expectedConfig); });
            }

            OneTimePasswordMembershipProvider otpMembershipProvider;

            using(this.repository.Playback())
            {
                otpMembershipProvider = new OneTimePasswordMembershipProvider();
                otpMembershipProvider.Initialize(name, config);
            }

            this.repository.BackToRecordAll();
            return otpMembershipProvider;
        }

        #endregion

        #region Tests for ValidateUser

        [TestMethod]
        public void ShouldReturnTrueWhenPasswordIsValid()
        {
            NameValueCollection config = new NameValueCollection();
            config.Add(OneTimePasswordMembershipProvider.ProviderTypeKey, typeof(MockMembershipProviderWrapper).AssemblyQualifiedName);
            config.Add(OneTimePasswordMembershipProvider.PasswordNumberOfDigitsKey, HmacOneTimePassword.DefaultNumberOfDigits.ToString());
            config.Add(OneTimePasswordMembershipProvider.PasswordAlgorithmKey, HmacOneTimePassword.DefaultAlgorithmName);
            config.Add(OneTimePasswordMembershipProvider.PasswordEpochKey, TimeOneTimePassword.DefaultEpoch.ToString());
            config.Add(OneTimePasswordMembershipProvider.PasswordTimeStepKey, TimeOneTimePassword.DefaultTimeStepSeconds.ToString());

            OneTimePasswordMembershipProvider otpMembershipProvider = this.CreateAndInitializeOneTimePasswordMembershipProvider("/", config);

            using(this.repository.Record())
            {
                MembershipUser membershipUser = new MockMembershipUser("username", "secret");
                Expect.Call(this.membershipProvider.GetUser("username", false)).Return(membershipUser);
            }

            using(this.repository.Playback())
            {
                string password = new TimeOneTimePassword("secret").GetPassword();
                Assert.IsTrue(otpMembershipProvider.ValidateUser("username", password));
            }
        }

        [TestMethod]
        public void ShouldReturnFalseWhenPasswordIsInvalid()
        {
            NameValueCollection config = new NameValueCollection();
            config.Add(OneTimePasswordMembershipProvider.ProviderTypeKey, typeof(MockMembershipProviderWrapper).AssemblyQualifiedName);
            config.Add(OneTimePasswordMembershipProvider.PasswordNumberOfDigitsKey, HmacOneTimePassword.DefaultNumberOfDigits.ToString());
            config.Add(OneTimePasswordMembershipProvider.PasswordAlgorithmKey, HmacOneTimePassword.DefaultAlgorithmName);
            config.Add(OneTimePasswordMembershipProvider.PasswordEpochKey, TimeOneTimePassword.DefaultEpoch.ToString());
            config.Add(OneTimePasswordMembershipProvider.PasswordTimeStepKey, TimeOneTimePassword.DefaultTimeStepSeconds.ToString());

            OneTimePasswordMembershipProvider otpMembershipProvider = this.CreateAndInitializeOneTimePasswordMembershipProvider("/", config);

            using(this.repository.Record())
            {
                MembershipUser membershipUser = new MockMembershipUser("username", "secret");
                Expect.Call(this.membershipProvider.GetUser("username", false)).Return(membershipUser);
            }

            using(this.repository.Playback())
            {
                string password = new TimeOneTimePassword("fault").GetPassword();
                Assert.IsFalse(otpMembershipProvider.ValidateUser("username", password));
            }
        }

        [TestMethod]
        public void ShouldReturnFalseWhenInternalExceptionIsThrown()
        {
            NameValueCollection config = new NameValueCollection();
            config.Add(OneTimePasswordMembershipProvider.ProviderTypeKey, typeof(MockMembershipProviderWrapper).AssemblyQualifiedName);
            config.Add(OneTimePasswordMembershipProvider.PasswordNumberOfDigitsKey, HmacOneTimePassword.DefaultNumberOfDigits.ToString());
            config.Add(OneTimePasswordMembershipProvider.PasswordAlgorithmKey, HmacOneTimePassword.DefaultAlgorithmName);
            config.Add(OneTimePasswordMembershipProvider.PasswordEpochKey, TimeOneTimePassword.DefaultEpoch.ToString());
            config.Add(OneTimePasswordMembershipProvider.PasswordTimeStepKey, TimeOneTimePassword.DefaultTimeStepSeconds.ToString());

            OneTimePasswordMembershipProvider otpMembershipProvider = this.CreateAndInitializeOneTimePasswordMembershipProvider("/", config);

            using(this.repository.Record())
            {
                MembershipUser membershipUser = new MockMembershipUserGetPasswordException("username");
                Expect.Call(this.membershipProvider.GetUser("username", false)).Return(membershipUser);
            }

            using(this.repository.Playback())
            {
                string password = new TimeOneTimePassword("fault").GetPassword();
                Assert.IsFalse(otpMembershipProvider.ValidateUser("username", password));
            }
        }

        #endregion
    }
}